'use strict'

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const fse = require('fs-extra')
const glob = require('glob')
const ejs = require('ejs')
const semver = require('semver')
const userHome = require('user-home')
const Command = require('@hjy-cli/command')
const Package = require('@hjy-cli/package')
const log = require('@hjy-cli/log')
const { spinnerStart, sleep, execAsync } = require('@hjy-cli/utils')

const getProjectTemplate = require('./getProjectTemplate')

const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'

const TEMPLATE_TYPE_NORMAL = 'normal'   // 标准模版
const TEMPLATE_TYPE_CUSTOM = 'custom'   // 自定义模版

const WHITE_COMMAND = ['npm', 'cnpm']

class InitCommand extends Command {
    init() {
        // 运行：hjy-cli init --targetPath D:/software/java/workspace/test-cli/hjy-cli/commands/init --debug --force test-project
        this.projectName = this._argv[0] || ''  // 判断项目名是否有传入
        this.force = !!this._cmd.force          // 判断--force是否有传入
        log.verbose('projectName', this.projectName)
        log.verbose('force', this.force)
    }

    async exec() {
        try {
            // 1、准备阶段
            const projectInfo = await this.prepare()
            if (projectInfo) {
                this.projectInfo = projectInfo
                // 2、下载模版
                await this.downloadTemplate()
                // 3、安装模版
                await this.installTemplate()
            }
        } catch (e) {
            log.error(e.message)
            if (process.env.LOG_LEVEL === 'verbose') {
                console.log(e);
            }
        }
    }

    async installTemplate () {
        log.verbose('templateInfo', this.templateInfo);
        if (this.templateInfo) {
            if (!this.templateInfo.type) {
                this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
            }
            if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
                // 标准安装
                await this.installNormalTemplate();
            } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
                // 自定义安装
                await this.installCustomTemplate();
            } else {
                throw new Error('无法识别项目模板类型！');
            }
        } else {
            throw new Error('项目模板信息不存在！');
        }
    }

    // 标准安装
    async installNormalTemplate() {
        log.verbose('templateNpm', this.templateNpm);
        // 拷贝模板代码至当前目录
        let spinner = spinnerStart('正在安装模板...');
        await sleep();
        try {
            const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
            const targetPath = process.cwd();   // 当前目录
            fse.ensureDirSync(templatePath);    // ensureDirSync确保当前目录是存在的
            fse.ensureDirSync(targetPath);
            fse.copySync(templatePath, targetPath); // 从templatePath拷贝到targetPath
        } catch (e) {
            throw e;
        } finally {
            spinner.stop(true);
            log.success(  '模板安装成功');
        }
        const templateIgnore = this.templateInfo.ignore || [];
        const ignore = ['**/node_modules/**', ...templateIgnore];
        await this.ejsRender({ ignore });
        const { installCommand, startCommand } = this.templateInfo;
        // 依赖安装
        await this.execCommand(installCommand, '依赖安装失败！');
        // 启动命令执行
        await this.execCommand(startCommand, '启动执行命令失败！');
    }

    // 自定义安装
    async installCustomTemplate() {
        // 查询自定义模板的入口文件
        if (await this.templateNpm.exists()) {
            const rootFile = this.templateNpm.getRootFilePath();
            if (fs.existsSync(rootFile)) {
                log.notice('开始执行自定义模板');
                const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
                const options = {
                    templateInfo: this.templateInfo,
                    projectInfo: this.projectInfo,
                    sourcePath: templatePath,
                    targetPath: process.cwd(),
                };
                const code = `require('${rootFile}')(${JSON.stringify(options)})`;
                log.verbose('code', code);
                await execAsync('node', ['-e', code], { stdio: 'inherit', cwd: process.cwd() });
                log.success('自定义模板安装成功');
            } else {
                throw new Error('自定义模板入口文件不存在！');
            }
        }
    }

    // 防止命令被修改
    checkCommand(cmd) {
        if (WHITE_COMMAND.includes(cmd)) {
            return cmd;
        }
        return null;
    }

    async execCommand(command, errMsg) {
        let ret;
        if (command) {
            const cmdArray = command.split(' ');
            const cmd = this.checkCommand(cmdArray[0]);
            if (!cmd) {
                throw new Error('命令不存在！命令：' + command);
            }
            const args = cmdArray.slice(1);
            ret = await execAsync(cmd, args, {
                stdio: 'inherit',
                cwd: process.cwd(),
            });
        }
        if (ret !== 0) {
            throw new Error(errMsg);
        }
        return ret;
    }

    async ejsRender(options) {
        const dir = process.cwd();
        const projectInfo = this.projectInfo;
        return new Promise((resolve, reject) => {
            glob('**', {
                cwd: dir,
                ignore: options.ignore || '',   // 忽略目录
                nodir: true,
            }, function(err, files) {
                if (err) {
                    reject(err);
                }
                Promise.all(files.map(file => {
                    const filePath = path.join(dir, file);
                    console.log('filePath =======>', filePath)
                    return new Promise((resolve1, reject1) => {
                        ejs.renderFile(filePath, projectInfo, {}, (err, result) => {
                            if (err) {
                                reject1(err);
                            } else {
                                // 重新写入
                                fse.writeFileSync(filePath, result);
                                resolve1(result);
                            }
                        });
                    });
                })).then(() => {
                    resolve();
                }).catch(err => {
                    reject(err);
                });
            });
        });
    }

    async prepare() {
        // 0、判断项目模版是否存在
        const template = await getProjectTemplate() // 请求后端接口
        if (!template || template.length === 0) {
            throw new Error('项目模板不存在');
        }
        this.template = template
        // 1、判断当前目录是否为空
        const localPath = process.cwd() // D:\software\java\workspace\test-cli\hjy-cli
        if (!this.isDirEmpty(localPath)) {
            let ifContinue = false
            // 如果有传入--force，此方法内不执行
            if (!this.force) {
                // 1.1、询问是否继续创建
                ifContinue = (await inquirer.prompt({
                    type: 'confirm',
                    name: 'ifContinue',
                    default: false,
                    message: '当前文件夹不为空，是否继续创建项目？'
                })).ifContinue
                if (!ifContinue) {
                    return
                }
            }
            if (ifContinue || this.force) {
                const { confirmDelete } = await inquirer.prompt({
                    type: 'confirm',
                    name: 'confirmDelete',
                    default: false,
                    message: '是否确认清空当前目录下的文件？'
                })
                // 2、是否启动强制更新
                if (confirmDelete) {
                    // 清空当前目录
                    fse.emptyDirSync('C:\\Users\\hjy\\Desktop\\1')
                    // fse.emptyDirSync(localPath)
                }
            }

        }

        return this.getProjectInfo()
    }

    /**
     * 1、选择创建项目或组件
     * 2、获取项目的基本信息
     * @returns {Promise<{}>} 项目基本信息（object）
     */
    async getProjectInfo() {
        function isValidName(v) {
            return /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v);
        }

        let projectInfo = {};
        let isProjectNameValid = false;
        if (isValidName(this.projectName)) {
            isProjectNameValid = true;
            projectInfo.projectName = this.projectName;
        }

        // 1. 选择创建项目或组件
        const { type } = await inquirer.prompt({
            type: 'list',
            name: 'type',
            message: '请选择初始化类型:',
            default: TYPE_PROJECT,
            choices: [{
                name: '项目',
                value: TYPE_PROJECT,
            }, {
                name: '组件',
                value: TYPE_COMPONENT,
            }],
        });
        log.verbose('type', type);

        // 判断是项目还是组件
        this.template = this.template.filter(template => template.tag.includes(type));
        const title = type === TYPE_PROJECT ? '项目' : '组件';

        // 项目名称
        const projectNamePrompt = {
            type: 'input',
            name: 'projectName',
            message: `请输入${title}名称`,
            default: '',
            validate: function(v) {
                const done = this.async();
                setTimeout(function() {
                    // 1.首字符必须为英文字符
                    // 2.尾字符必须为英文或数字，不能为字符
                    // 3.字符仅允许"-_"
                    if (!isValidName(v)) {
                        done(`请输入合法的${title}名称`);
                        return;
                    }
                    done(null, true);
                }, 0);
            },
            filter: function(v) {
                return v;
            },
        };

        const projectPrompt = [];
        if (!isProjectNameValid) {
            projectPrompt.push(projectNamePrompt);
        }

        // 版本号
        projectPrompt.push(
            {
                type: 'input',
                name: 'projectVersion',
                message: `请输入${title}版本号`,
                default: '1.0.0',
                validate: function(v) {
                    const done = this.async();
                    setTimeout(function() {
                        // semver.valid校验输入的版本号是否合法
                        // 双感叹号转成boolean型
                        if (!(!!semver.valid(v))) {
                            done('请输入合法的版本号');
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
                filter: function(v) {
                    if (!!semver.valid(v)) {
                        return semver.valid(v);
                    } else {
                        return v;
                    }
                },
            },
            {
                type: 'list',
                name: 'projectTemplate',
                message: `请选择${title}模板`,
                choices: this.createTemplateChoice(),   // 返回用户填写的信息
            });

        if (type === TYPE_PROJECT) {    // 创建项目
            // 2. 获取项目的基本信息
            const project = await inquirer.prompt(projectPrompt);
            projectInfo = {
                ...projectInfo,
                type,
                ...project,
            };
        } else if (type === TYPE_COMPONENT) {   // 创建项目
            // 描述信息
            const descriptionPrompt = {
                type: 'input',
                name: 'componentDescription',
                message: '请输入组件描述信息',
                default: '',
                validate: function(v) {
                    const done = this.async();
                    setTimeout(function() {
                        if (!v) {
                            done('请输入组件描述信息');
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
            };
            projectPrompt.push(descriptionPrompt);

            // 2. 获取组件的基本信息
            const component = await inquirer.prompt(projectPrompt);
            projectInfo = {
                ...projectInfo,
                type,
                ...component,
            };
        }
        // 生成classname
        if (projectInfo.projectName) {
            projectInfo.name = projectInfo.projectName;
            projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '');
        }
        if (projectInfo.projectVersion) {
            projectInfo.version = projectInfo.projectVersion;
        }
        if (projectInfo.componentDescription) {
            projectInfo.description = projectInfo.componentDescription;
        }
        return projectInfo;
    }

    /**
     * 下载模版
     * 1、通过项目模版API获取项目模版信息
     * 1.1、通过egg.js搭建一套后端系统
     * 1.2、通过npm存储项目模版（vue-cli/vue-element-admin）
     * 1.3、讲项目模版信息存储到mongodb数据库中
     * 1.4、通过egg.js获取mongodb中的数据并且通过API返回
     *
     * @returns {Promise<void>}
     */
    async downloadTemplate() {
        console.log('12', this.projectInfo)
        const { projectTemplate } = this.projectInfo;
        // 用户选择的模版
        const templateInfo = this.template.find(item => item.npmName === projectTemplate);
        // 缓存目录
        const targetPath = path.resolve(userHome, '.hjy-cli', 'template');
        // node_modules路径
        const storeDir = path.resolve(userHome, '.hjy-cli', 'template', 'node_modules');
        const { npmName, version } = templateInfo;
        this.templateInfo = templateInfo;
        // 当前模块信息
        const templateNpm = new Package({
            targetPath,
            storeDir,
            packageName: npmName,
            packageVersion: version,
        });
        if (!await templateNpm.exists()) {
            const spinner = spinnerStart('正在下载模板...');
            await sleep();
            try {
                await templateNpm.install();
            } catch (e) {
                throw e;
            } finally {
                spinner.stop(true);
                if (await templateNpm.exists()) {
                    log.success('下载模板成功');
                    this.templateNpm = templateNpm;
                }
            }
        } else {
            const spinner = spinnerStart('正在更新模板...');
            await sleep();
            try {
                await templateNpm.update();
            } catch (e) {
                throw e;
            } finally {
                spinner.stop(true);
                if (await templateNpm.exists()) {
                    log.success('更新模板成功');
                    this.templateNpm = templateNpm;
                }
            }
        }
    }

    // 判断当前目录是否为空
    isDirEmpty(localPath) {
        // 读取D:\software\java\workspace\test-cli\hjy-cli下的所有文件
        let fileList = fs.readdirSync(localPath)
        // 文件过滤的逻辑
        fileList = fileList.filter(file => (
            !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
        ))
        return !fileList || fileList.length <= 0
    }

    // 返回用户填写的信息
    createTemplateChoice() {
        return this.template.map(item => ({
            value: item.npmName,
            name: item.name,
        }));
    }
}


function init(argv) {
    return new InitCommand(argv)
}

module.exports = init
module.exports.InitCommand = InitCommand
